package edu.ucla.cs.rpc.multicast;

import java.io.IOException;
import java.lang.reflect.Method;

import edu.ucla.cs.rpc.multicast.handlers.MessageHandler;
import edu.ucla.cs.rpc.multicast.network.message.Message;
import edu.ucla.cs.rpc.multicast.network.message.RPCMessage;
import edu.ucla.cs.rpc.multicast.network.message.Message.MessageType;

/**
 * RPCServer waits for incoming RPCClient method invocation requests, sets up
 * the method call, and invokes the method. No result is returned to the client.
 * 
 * @author Chase Covello Philip Russell
 * 
 */
public class RPCServer {

	private class RPCServerMessageHandler implements MessageHandler {

		public void receive(Message message) {
			if (message.getType() == MessageType.RPC_MESSAGE) {
				RPCMessage rpcMessage = (RPCMessage) message;
				invoke(rpcMessage);
			}
		}

	}

	// Dest implementation: fixed, moving, privilege, comm. history, or dest.
	// agreement
	private Dest dest;

	/**
	 * Constructs a new RPCServer with the given multicast Dest implementation.
	 * 
	 * @param dest
	 *            the Dest to use.
	 * @throws IOException
	 *             in the case of unrecoverable network errors.
	 */
	public RPCServer(Dest dest) throws IOException {
		this.dest = dest;
		dest.setMessageHandler(new RPCServerMessageHandler());
	}

	private void invoke(RPCMessage message) {
		try {
			Class clazz;
			Object object = message.getObject();
			if (object instanceof Class) {
				clazz = (Class) object;
				object = null;
			} else {
				clazz = object.getClass();
			}

			Method method = clazz.getMethod(message.getMethodName(), message
					.getArgTypes());
			method.invoke(object, message.getArgs());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Shuts down any threads created by this server. This method <b>must</b>
	 * be called before disposing of all references to this object; otherwise,
	 * its thread will continue to run until the application is forcibly
	 * terminated.
	 */
	public void shutdown() {
		dest.shutdown();
	}
}
